#include "pleiades.h"
#include "jni_helper.h"
#include "list.h"
#include <pthread.h>

/* global event handler container */
struct list_head evHandlerList;
pthread_mutex_t evHandlerListLock = PTHREAD_MUTEX_INITIALIZER;
pthread_once_t evHandlerListInitOnce = PTHREAD_ONCE_INIT;
static void initEventHandlerList(void)
{
	INIT_LIST_HEAD(&evHandlerList);
}

/* for handle level thread safety */
//pthread_mutex_t creationLock = PTHREAD_MUTEX_INITIALIZER;

/* Some frequently used Java paths */
#define PLEIADES_CONF     "com/nhncorp/pleiades/conf/PleiadesConfiguration"
#define PLEIADES_FACTORY	"com/nhncorp/pleiades/client/LockServiceClientFactory"
#define PLEIADES_EVENT_FACTORY	"com/nhncorp/pleiades/client/LockEventHandlerFactory"
#define PLEIADES_CLIENT	"com/nhncorp/pleiades/client/LockService"
#define PLEIADES_USERINFO "com/nhncorp/pleiades/client/PleiadesUser"
#define PLEIADES_EVENT_HANDLER	"com/nhncorp/pleiades/client/LockEventHandler"
#define PLEIADES_LOCKSTATUS	"com/nhncorp/pleiades/persistence/LockStatus"
#define PLEIADES_METADATA	"com/nhncorp/pleiades/protocol/LockMetaData"
#define PLEIADES_EVENTDATA	"com/nhncorp/pleiades/client/LockEventData"

/* Macros for constructing method signatures */
#define JPARAM(X)           "L" X ";"
#define JARRPARAM(X)        "[L" X ";"
#define JMETHOD1(X, R)      "(" X ")" R
#define JMETHOD2(X, Y, R)   "(" X Y ")" R
#define JMETHOD3(X, Y, Z, R)   "(" X Y Z")" R

static int getLockStatus(JNIEnv *env, jobject jClient, 
						jobject jLockStatus, pleiades_lock_status_t *lockStatus);
static int getLockMetaData(JNIEnv *env, jobject jClient, 
						jobject jMetaData, pleiades_lock_meta_data_t *metaData);
static int setLockMetaData(JNIEnv *env, jobject jClient, 
						jobject jMeta, const char *path, const char *key);

int getLockEventData(JNIEnv *env, jobject jHandler, 
					jobject jEventData, pleiades_lock_event_data_t *eventData);
/**
 * Helper function to destroy a local reference of java.lang.Object
 * @param env: The JNIEnv pointer. 
 * @param jFile: The local reference of java.lang.Object object
 * @return None.
 */
static void destroyLocalReference(JNIEnv *env, jobject jObject)
{
  if (jObject)
    (*env)->DeleteLocalRef(env, jObject);
}


/**
 * Helper function to create a com.nhncorp.pleiades.client.LockUserInfo object.
 * @param env: The JNIEnv pointer. 
 * @param sname: The service name for which to construct com.nhncorp.pleiades.client.LockUserInfo
 * @param hname: The host name(default to system hostname) for which to construct com.nhncorp.pleiades.client.LockUserInfo
 * object.
 * @return Returns a jobject on success and NULL on error.
 */
static jobject constructNewObjectOfUserInfo(JNIEnv *env, 
							const char *sname, const char *hname)
{
    //Construct a java.lang.String object
    if(sname == NULL) {
        fprintf(stderr, "service name should be neither NULL nor empty\n");
        errno = EINTERNAL;
        return NULL;    
    }

    jstring jServiceName = (*env)->NewStringUTF(env, sname);
    jstring jHostName = NULL;
    
    jobject jUserInfo = NULL;
    if(hname == NULL) {
    	jUserInfo = constructNewObjectOfClass(env, 
				"com/nhncorp/pleiades/client/PleiadesUser", 
				"(Ljava/lang/String;)V", jServiceName);        
    } 
	else {
    	jHostName = (*env)->NewStringUTF(env, hname);
    	jUserInfo = constructNewObjectOfClass(env, 
				"com/nhncorp/pleiades/client/PleiadesUser", 
				JMETHOD2(JPARAM("java/lang/String"), "S", "V"), 
				jServiceName, jHostName);    
    }

    if (jUserInfo == NULL) {
        fprintf(stderr, "Can't construct instance of class "
                "com.nhncorp.pleiades.client.PleiadesUser for %s\n", 
				sname);
        errno = EINTERNAL;
        return NULL;
    }

    // Destroy the local reference to the java.lang.String object
    destroyLocalReference(env, jServiceName);
    if(jHostName != NULL) 
		destroyLocalReference(env, jHostName);    

    return jUserInfo;
}

/**
 * Helper function to create a com.nhncorp.pleiades.protocol.pleiades_lock_meta_data_t object.
 * @param env: The JNIEnv pointer. 
 * @return Returns a jobject on success and NULL on error.
 */
static jobject constructNewObjectOfMetaData(JNIEnv *env, const char* path)
{
	jstring jLockId = (*env)->NewStringUTF(env, path);

    //Construct the com.nhncorp.pleiades.protocol.pleiades_lock_meta_data_t object
    jobject jMetaData = constructNewObjectOfClass(env, PLEIADES_METADATA, 
			"(Ljava/lang/String;)V", jLockId);

    if (jMetaData == NULL) {
        fprintf(stderr, "Can't construct instance of class "
                "com.nhncorp.pleiades.protocol.pleiades_lock_meta_data_t for n");
        errno = EINTERNAL;
        return NULL;
    }    

    destroyLocalReference(env, jLockId);
    
    return jMetaData;
}

pleiades_client_t *pleiades_create_client(const char* service, 
										const char* ownerId) 
{
    JNIEnv *env = 0;
    jobject jConfiguration = NULL;
    jobject jPleiadesClient = NULL;
    jobject jUserInfo = NULL;
    jobject gClientRef = NULL;
	jvalue  jVal;
	pleiades_client_t *pleClient;

    // Get the JNIEnv* corresponding to current thread
    env = getJNIEnv();

    // Create the com.nhncorp.pleiades.conf.PleiadesConfiguration 
	// object
    jConfiguration = constructNewObjectOfClass(env, PLEIADES_CONF, "()V");
    if (jConfiguration == NULL) {
        fprintf(stderr, "Can't construct instance of class "
                "com.nhncorp.pleiades.conf.PleiadesConfiguration\n");
        errno = EINTERNAL;
        return NULL;
    }
    
    // Create the com.nhncorp.pleiades.conf.PleiadesConfiguration 
	// object
    jUserInfo =
        constructNewObjectOfUserInfo(env, service, ownerId);
        
    if (jUserInfo == NULL) {
        fprintf(stderr, "Can't construct instance of class "
                "com.nhncorp.pleiades.client.LockUserInfo\n");
        errno = EINTERNAL;
		destroyLocalReference(env, jConfiguration);        
        return NULL;
    }        
    
	// jPleiadesClient = 
	// 		LockServiceClientFactory::newInstance(userInfo, conf);
	//pthread_mutex_lock(&creationLock);
    if (invokeMethod(env, &jVal, STATIC, NULL, PLEIADES_FACTORY, 
				"newInstance", JMETHOD2(JPARAM(PLEIADES_USERINFO), 
				JPARAM(PLEIADES_CONF), JPARAM(PLEIADES_CLIENT)), 
				jUserInfo, jConfiguration) != 0) {
	    fprintf(stderr, "Call to com.nhncorp.pleiades."
    	       "LockServiceClientFactory::newInstance failed!\n");
     	errno = EINTERNAL;
		//pthread_mutex_unlock(&creationLock);
 		goto done;
	}
	//pthread_mutex_unlock(&creationLock);

	jPleiadesClient = jVal.l;
    
done:
    // Release unnecessary local references
    destroyLocalReference(env, jConfiguration);
    destroyLocalReference(env, jUserInfo);

    /* Create a global reference for pleiades client */
    if (jPleiadesClient) {
        gClientRef = (*env)->NewGlobalRef(env, jPleiadesClient);
        destroyLocalReference(env, jPleiadesClient);
    }

	/* alloc pleiades_client_t structure and assign its reference */
	pleClient = malloc(sizeof(pleiades_client_t));
	pleClient->obj = gClientRef;

	/* initialize global handle list once */
	pthread_once(&evHandlerListInitOnce, initEventHandlerList);

    return pleClient;    
}

pleiades_event_handler_t *pleiades_create_event_handler()
{
    JNIEnv *env = 0;
    jobject jConfiguration = NULL;
    jobject jPleiadesEventHandler = NULL;
    jobject gEventHandlerRef = NULL;
	jvalue  jVal;
	pleiades_event_handler_t *handler;

    // Get the JNIEnv* corresponding to current thread
    env = getJNIEnv();

    // Create the com.nhncorp.pleiades.conf.PleiadesConfiguration 
	// object
    jConfiguration = constructNewObjectOfClass(env, PLEIADES_CONF, "()V");
    if (jConfiguration == NULL) {
        fprintf(stderr, "Can't construct instance of class "
                "com.nhncorp.pleiades.conf.PleiadesConfiguration\n");
        errno = EINTERNAL;
        return NULL;
    }
    
	// jPleiadesEventHandler = LockEventHandlerFactory::newInstance(conf);
    if (invokeMethod(env, &jVal, STATIC, NULL, PLEIADES_EVENT_FACTORY, 
				"newInstance", JMETHOD1(JPARAM(PLEIADES_CONF), 
				JPARAM(PLEIADES_EVENT_HANDLER)), jConfiguration) != 0) {
	    fprintf(stderr, "Call to com.nhncorp.pleiades."
    	       "LockEventHandlerFactory::newInstance failed!\n");
     	errno = EINTERNAL;
 		goto done;
	}

	jPleiadesEventHandler = jVal.l;
	
done:
    // Release unnecessary local references
    destroyLocalReference(env, jConfiguration);
    

    /* Create a global reference for pleiades client */
    if (jPleiadesEventHandler) {
        gEventHandlerRef = (*env)->NewGlobalRef(env, jPleiadesEventHandler);
        destroyLocalReference(env, jPleiadesEventHandler);
    }

	handler = calloc(1, sizeof(pleiades_event_handler_t));
	handler->obj = gEventHandlerRef;

	INIT_LIST_HEAD(&handler->event_monitors);

	pthread_mutex_lock(&evHandlerListLock);
	list_add_tail(&handler->link, &evHandlerList);
	pthread_mutex_unlock(&evHandlerListLock);

    return handler;    
}

void pleiades_close_event_handler(pleiades_event_handler_t *pleHandler)
{
	pleiades_lock_event_monitor_t *pos, *n;

	pthread_mutex_lock(&evHandlerListLock);
	list_del(&pleHandler->link);
	pthread_mutex_unlock(&evHandlerListLock);

	list_for_each_entry_safe(pos, n, &pleHandler->event_monitors,
			pleiades_lock_event_monitor_t,link) {
		list_del(&pos->link);
		pleiades_destroy_lock_event_filter(pos->filter);
		free(pos);
	}

	free(pleHandler);
}

int pleiades_get_lock(pleiades_client_t *pleClient, const char* path, 
					pleiades_lock_mode_t mode)
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
    // Construct a java.lang.String object
    jstring jPathString = (*env)->NewStringUTF(env, path);
    jshort jMode = (short)mode;
    
    // getLock
    jvalue jVal;
    if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT, 
				"getLock", JMETHOD2(JPARAM("java/lang/String"), "S", "Z"), 
				jPathString, jMode) != 0) {
	    fprintf(stderr, "Call to com.nhncorp.pleiades."
    	       "client.LockService::getLock failed!\n");
     	errno = EINTERNAL;
        return -1;
    }    

    // Destroy the local reference to the java.lang.String object
    destroyLocalReference(env, jPathString);
    return (jVal.z) ? 0 : -1;
}

int pleiades_try_lock(pleiades_client_t *pleClient, const char* path, 
					pleiades_lock_mode_t mode)
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
    // Construct a java.lang.String object
    jstring jPathString = (*env)->NewStringUTF(env, path);
    jshort jMode = (short)mode;
    
    // tryLock
    jvalue jVal;
    if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT, 
				"tryLock", JMETHOD2(JPARAM("java/lang/String"), "S", "Z"), 
				jPathString, jMode) != 0) {
	    fprintf(stderr, "Call to com.nhncorp.pleiades."
    	       "client.LockService::getLock failed!\n");
     	errno = EINTERNAL;
        return -1;
    }    

    // Destroy the local reference to the java.lang.String object
    destroyLocalReference(env, jPathString);
    return (jVal.z) ? 0 : -1;
}

int pleiades_lock_exist(pleiades_client_t *client, const char* path) 
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
	// Construct a java.lang.String object
    jstring jPathString = (*env)->NewStringUTF(env, path);
    
    jvalue  jVal;  
    // lockExist  
    if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT, 
				"lockExist", JMETHOD1(JPARAM("java/lang/String"), "Z"), jPathString) != 0) {
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::exist failed!\n");
        errno = EINTERNAL;
        destroyLocalReference(env, jPathString);
        return -1;
    }
    
    destroyLocalReference(env, jPathString);
    return (jVal.z) ? 0 : -1;
}

pleiades_lock_status_t *pleiades_list_lock( pleiades_client_t *client, 
						const char* path, int *num_entries)
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
    pleiades_lock_status_t *lockStatusList = 0;
    jobjectArray jLockStatusList = NULL;
    
    // Construct a java.lang.String object
    jstring jPathString = (*env)->NewStringUTF(env, path);
    
    // listLock
    jvalue jVal;
    if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT, 
				"listLock", JMETHOD1(JPARAM("java/lang/String"), JARRPARAM(PLEIADES_LOCKSTATUS)), 
				jPathString) != 0) { 
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::listLock failed!\n");
        errno = EINTERNAL;
        destroyLocalReference(env, jPathString);
        return NULL;
    }
    
    if(jVal.l == NULL) {
        fprintf(stderr, "PLEIADES_LOCKSTATUS_LIST is NULL\n");
        errno = EINTERNAL;
        destroyLocalReference(env, jPathString);
        return NULL;    
    }         
    
    jLockStatusList = jVal.l;
    
    // Figure out no of entries in that directory
    jsize jLockStatusListSize = (*env)->GetArrayLength(env, jLockStatusList);
    *numEntries = jLockStatusListSize;
    if (jLockStatusListSize == 0) {
        errno = 0;
        goto done;
    }
    
    // Allocate memory
    lockStatusList = calloc(jLockStatusListSize, sizeof(pleiades_lock_status_t));
    if (lockStatusList == NULL) {
        errno = ENOMEM;
        goto done;
    }

    // Save metaData information in metaDataList
    jsize idx;
    jobject tmpLockStatus;
    for (idx=0; idx < jLockStatusListSize; ++idx) {
        tmpLockStatus = (*env)->GetObjectArrayElement(env, jLockStatusList, idx);
        if (getLockMetaData(env, jClient, tmpLockStatus, &lockStatusList[idx])) {
            errno = EINTERNAL;
            pleiades_free_lock_status(lockStatusList, jLockStatusListSize);
            destroyLocalReference(env, tmpLockStatus);
            lockStatusList = NULL;
            goto done;
        }

        destroyLocalReference(env, tmpLockStatus);
    }
    
done:
    // Delete unnecessary local references
	destroyLocalReference(env, jPathString);
    destroyLocalReference(env, jLockStatusList);	

    return lockStatusList;							
}

int pleiades_release_lock(pleiades_client_t *pleClient, const char* path, 
					pleiades_lock_mode_t mode)
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
    // Construct a java.lang.String object
    jstring jPathString = (*env)->NewStringUTF(env, path);
    jshort jMode = (short)mode;
    
    // releaseLock
    jvalue jVal;
    if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT, 
				"releaseLock", 
				JMETHOD2(JPARAM("java/lang/String"), "S", "Z"), 
				jPathString, jMode) != 0) {
	    fprintf(stderr, "Call to com.nhncorp.pleiades."
    	       "client.LockService::getLock failed!\n");
     	errno = EINTERNAL;
        return -1;
    }    

    // Destroy the local reference to the java.lang.String object
    destroyLocalReference(env, jPathString);
    return (jVal.z) ? 0 : -1;
}

int pleiades_get_contents(pleiades_client_t *pleClient, const char* path, 
						char* contents, int contents_buf_size)
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
	// Construct a java.lang.String object
    jstring jPathString = (*env)->NewStringUTF(env, path);
    
    // getContents
    jvalue jVal;
    if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT, 
				"getContents", "(Ljava/lang/String;)Ljava/lang/String;", 
				jPathString) != 0) { 
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::getContents failed!\n");
        errno = EINTERNAL;
        return -1;
    }
    
	jstring     jContents = jVal.l;
    const char *cContents = 
		(const char*) ((*env)->GetStringUTFChars(env, jContents, NULL));    
    strncpy(contents ,cContents, contents_buf_size); 
    (*env)->ReleaseStringUTFChars(env, jContents, cContents);
    destroyLocalReference(env, jContents);
    
    return 0;
}

int pleiades_set_contents(pleiades_client_t *pleClient, const char* path, 
		const char* contents)
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
	// Construct a java.lang.String object
    jstring jPathString = (*env)->NewStringUTF(env, path);
    jstring jContents = (*env)->NewStringUTF(env, contents);
    
    // setContents
    jvalue jVal;
    if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT, 
				"setContents", JMETHOD2(JPARAM("java/lang/String"), 
				JPARAM("java/lang/String"), "Z"), 
				jPathString, jContents) != 0) { 
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::setContents failed!\n");
        errno = EINTERNAL;
        return -1;
    }
    
    destroyLocalReference(env, jPathString);
    destroyLocalReference(env, jContents);
    
    return (jVal.z) ? 0 : -1;
}

int pleiades_create_file_node(pleiades_client_t *pleClient, const char* path, 
					const char* key, bool overWrite) {

    //Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
    jobject jMetaData = constructNewObjectOfMetaData(env, path);
    if(jMetaData == NULL)
    	return -2;

    if( setLockMetaData(env, jClient, jMetaData, path, key))
    	return -2;
    
    // createNode
    jvalue jVal;
    if (overWrite == true) {
    	jboolean jOverWrite = overWrite;
    	if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT, 
					"createNode", JMETHOD2(JPARAM(PLEIADES_METADATA), 
					"Z", "Z"), jMetaData, jOverWrite) != 0) { 
        	fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::createNode failed!\n");
        	errno = EINTERNAL;
        	return -1;
    	}
    } 
	else {
    	if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT,
        	             "createNode", JMETHOD1(JPARAM(PLEIADES_METADATA), 
						 "Z"), jMetaData) != 0) { 
        	fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::createNode failed!\n");
        	errno = EINTERNAL;
        	return -1;
    	}
    }
    
    return (jVal.z) ? 0 : -1;
}

int pleiades_create_dir_node(pleiades_client_t *pleClient, const char* path, 
						const char* key) {
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
    jobject jMetaData = constructNewObjectOfMetaData(env, path);
    if (jMetaData == NULL)
    	return -2;

    if (setLockMetaData(env, jClient, jMetaData, path, key))
    	return -2;
    
    // createDNode
    jvalue jVal;
    jboolean jOverWrite = false;
    jboolean jDir = true;
   	if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT,
       	             "createNode", JMETHOD3(JPARAM(PLEIADES_METADATA), 
					 "Z", "Z", "Z"), jMetaData, jOverWrite, jDir) != 0) { 
       	fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::createNode failed!\n");
       	errno = EINTERNAL;
       	return -1;
   	}
    
    return (jVal.z) ? 0 : -1;
}

int pleiades_delete_node(pleiades_client_t *pleClient, const char* path, 
					bool recursive) 
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
	// Construct a java.lang.String object
    jstring jPathString = (*env)->NewStringUTF(env, path);
    jboolean jRecursive = recursive;
    
    // deleteNode
    jvalue jVal;
    if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT, 
				"deleteNode", JMETHOD2(JPARAM("java/lang/String"),"Z", "Z"), 
				jPathString, jRecursive) != 0) { 
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::deleteNode failed!\n");
        errno = EINTERNAL;
        destroyLocalReference(env, jPathString);
        return -1;
    }
    
    destroyLocalReference(env, jPathString);
    
    return (jVal.z) ? 0 : -1;
}

int pleiades_node_exist(pleiades_client_t *pleClient, const char* path) 
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
	// Construct a java.lang.String object
    jstring jPathString = (*env)->NewStringUTF(env, path);
    
    jvalue  jVal;  
    // exist  
    if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT, 
				"exist", JMETHOD1(JPARAM("java/lang/String"), "Z"), jPathString) != 0) {
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::exist failed!\n");
        errno = EINTERNAL;
        destroyLocalReference(env, jPathString);
        return -1;
    }
    
    destroyLocalReference(env, jPathString);
    return (jVal.z) ? 0 : -1;
}

pleiades_lock_meta_data_t *pleiades_get_meta_data(pleiades_client_t *pleClient,
	   								const char* path) 
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
	// Construct a java.lang.String object
    jstring jPathString = (*env)->NewStringUTF(env, path);
    jobject jMetaData;
	pleiades_lock_meta_data_t *metaData;
    
    jvalue  jVal;  
    // getMetaData  
    if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT, 
				"getMetaData", JMETHOD1(JPARAM("java/lang/String"), 
				JPARAM(PLEIADES_METADATA)), jPathString) != 0) {
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::getMetaData failed!\n");
        errno = EINTERNAL;
        destroyLocalReference(env, jPathString);
        return NULL;
    }
    
    if(jVal.l == NULL) {
        fprintf(stderr, "PLEIADES_METADATA is NULL\n");
        errno = EINTERNAL;
        destroyLocalReference(env, jPathString);
        return NULL;    
    }
        
    jMetaData = jVal.l;
    
    metaData = calloc(1, sizeof(pleiades_lock_meta_data_t));
    if (getLockMetaData(env, jClient, jMetaData, metaData)) {
        pleiades_free_lock_meta_data(metaData, 1);
        metaData = NULL;
        goto done;
    }
    
	done:
	
	// Delete unnecessary local references
    destroyLocalReference(env, jPathString);
    return metaData;
}

pleiades_lock_meta_data_t *pleiades_get_children_meta_data(
			pleiades_client_t *pleClient, const char* path, int *numEntries, 
			bool includeDir) 
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
    pleiades_lock_meta_data_t *metaDataList = 0;
    jobjectArray jMetaDataList = NULL;
    
    // Construct a java.lang.String object
    jstring jPathString = (*env)->NewStringUTF(env, path);
    jboolean jIncludeDir = includeDir;
    
    // getChildrenMetaData
    jvalue jVal;
    if (invokeMethod(env, &jVal, INSTANCE, jClient, PLEIADES_CLIENT, 
				"getMetaDataList", JMETHOD2(JPARAM("java/lang/String"),"Z", 
				JARRPARAM(PLEIADES_METADATA)), 
				jPathString, jIncludeDir) != 0) { 
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::getMetaDataList failed!\n");
        errno = EINTERNAL;
        destroyLocalReference(env, jPathString);
        return NULL;
    }
    
    if(jVal.l == NULL) {
        fprintf(stderr, "PLEIADES_METADATA_LIST is NULL\n");
        errno = EINTERNAL;
        destroyLocalReference(env, jPathString);
        return NULL;    
    }         
    
    jMetaDataList = jVal.l;
    
    // Figure out no of entries in that directory
    jsize jMetaDataListSize = (*env)->GetArrayLength(env, jMetaDataList);
    *numEntries = jMetaDataListSize;
    if (jMetaDataListSize == 0) {
        errno = 0;
        goto done;
    }
    
    // Allocate memory
    metaDataList = calloc(jMetaDataListSize, sizeof(pleiades_lock_meta_data_t));
    if (metaDataList == NULL) {
        errno = ENOMEM;
        goto done;
    }

    // Save metaData information in metaDataList
    jsize idx;
    jobject tmpMetaData;
    for (idx=0; idx < jMetaDataListSize; ++idx) {
        tmpMetaData = (*env)->GetObjectArrayElement(env, jMetaDataList, idx);
        if (getLockMetaData(env, jClient, tmpMetaData, &metaDataList[idx])) {
            errno = EINTERNAL;
            pleiades_free_lock_meta_data(metaDataList, jMetaDataListSize);
            destroyLocalReference(env, tmpMetaData);
            metaDataList = NULL;
            goto done;
        }

        destroyLocalReference(env, tmpMetaData);
    }
    
done:
    // Delete unnecessary local references
	destroyLocalReference(env, jPathString);
    destroyLocalReference(env, jMetaDataList);	

    return metaDataList;    
}

pleiades_lock_event_data_t *pleiades_read_event(
									pleiades_event_handler_t *handler) 
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jHandler = (jobject)handler->obj;
    
    jobject jEventData;
    jvalue  jVal;
	pleiades_lock_event_data_t *eventData;

	// readEvent
    if (invokeMethod(env, &jVal, INSTANCE, jHandler, 
				PLEIADES_EVENT_HANDLER, "readEvent", 
				JMETHOD1("", JPARAM(PLEIADES_EVENTDATA)) ) != 0) {
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockEventHandler::readEvent failed!\n");
        errno = EINTERNAL;
        return NULL;
    }
    
    if (jVal.l == NULL) {
        fprintf(stderr, "PLEIADES_EVENTDATA is NULL\n");
        errno = EINTERNAL;
        return NULL;    
    }  

    jEventData = jVal.l;     
    eventData = calloc(1, sizeof(pleiades_lock_event_data_t));
    
    if (getLockEventData(env, jHandler, jEventData, eventData)) {
        pleiades_free_lock_event_data(eventData, 1);
        eventData = NULL;
        goto done;
    }
    
done:
    return eventData;
}

pleiades_lock_event_data_t *pleiades_take_event(
							pleiades_event_handler_t *handler) 
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jHandler = (jobject)handler->obj;
    
    jobject jEventData;
    jvalue  jVal;
	pleiades_lock_event_data_t *eventData;

	// readEvent
    if (invokeMethod(env, &jVal, INSTANCE, jHandler, 
				PLEIADES_EVENT_HANDLER, "takeEvent", 
				JMETHOD1("", JPARAM(PLEIADES_EVENTDATA)) ) != 0) {
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockEventHandler::readEvent failed!\n");
        errno = EINTERNAL;
        return NULL;
    }
    
    if (jVal.l == NULL) {
        fprintf(stderr, "PLEIADES_EVENTDATA is NULL\n");
        errno = EINTERNAL;
        return NULL;    
    }  

    jEventData = jVal.l;     
    eventData = calloc(1, sizeof(pleiades_lock_event_data_t));
    
    if (getLockEventData(env, jHandler, jEventData, eventData)) {
        pleiades_free_lock_event_data(eventData, 1);
        eventData = NULL;
        goto done;
    }
    
done:
    return eventData;
}

pleiades_lock_event_data_t *pleiades_take_multi_event(
		pleiades_event_handler_t *handler, int *numEntries, int maxCount) 
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jHandler = (jobject)handler->obj;
    
	pleiades_lock_event_data_t *eventDataList = 0;
	jobjectArray jEventDataList = NULL;
	jint jMaxCount = maxCount;
	jsize jEventDataListSize;
	
    // takeMultiEvent
    jvalue jVal;
    if (invokeMethod(env, &jVal, INSTANCE, jHandler, 
				PLEIADES_EVENT_HANDLER, "takeMultiEvent", 
				JMETHOD1("I", JARRPARAM(PLEIADES_EVENTDATA)), 
				jMaxCount) != 0) { 
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockEventHandler::takeMultiEvent failed!\n");
        errno = EINTERNAL;
        return NULL;
    }
    
    jEventDataList = jVal.l;
    
    // Figure out no of entries in that directory
    jEventDataListSize = (*env)->GetArrayLength(env, jEventDataList);
    *numEntries = jEventDataListSize;
    if (jEventDataListSize == 0) {
        errno = 0;
        goto done;
    }
    
    // Allocate memory
    eventDataList = calloc(jEventDataListSize, 
							sizeof(pleiades_lock_event_data_t));
    if (eventDataList == NULL) {
        errno = ENOMEM;
        goto done;
    }

    // Save eventData information in eventDataList
    jsize idx;
    jobject tmpEventData;
    for (idx=0; idx < jEventDataListSize; ++idx) {
        tmpEventData = (*env)->GetObjectArrayElement(env, 
											jEventDataList, idx);
        if (getLockEventData(env, jHandler, tmpEventData, 
							&eventDataList[idx])) {
            errno = EINTERNAL;
            pleiades_free_lock_event_data(eventDataList, jEventDataListSize);
            destroyLocalReference(env, tmpEventData);
            eventDataList = NULL;
            goto done;
        }

        destroyLocalReference(env, tmpEventData);
    }
    
done:
    // Delete unnecessary local references
    destroyLocalReference(env, jEventDataList);	

    return eventDataList;
}

int pleiades_poison(pleiades_client_t *pleClient) 
{

    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
    // poison
	if (invokeMethod(env, NULL, INSTANCE, jClient, PLEIADES_CLIENT, 
				"poison", "()V") != 0) { 
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::poison failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    return 0;
}

int pleiades_restart_session(pleiades_client_t *pleClient) 
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
    // restartSession
	if (invokeMethod(env, NULL, INSTANCE, jClient, PLEIADES_CLIENT, 
				"restartSession", "()V") != 0) { 
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::restartSession failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    return 0;
}

int pleiades_close_client(pleiades_client_t *pleClient) 
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    
    // close
	if (invokeMethod(env, NULL, INSTANCE, jClient, PLEIADES_CLIENT, 
				"close", "()V") != 0) { 
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::close failed!\n");
        errno = EINTERNAL;
        return -1;
    }

	free(pleClient);
    
    return 0;    
}

int pleiades_set_event_handler(pleiades_client_t *pleClient, 
						pleiades_event_handler_t *handler, int eventMask)
{
    // Get the JNIEnv* corresponding to current thread
    JNIEnv* env = getJNIEnv();
    jobject jClient = (jobject)pleClient->obj;
    jobject jHandler = (jobject)handler->obj;
    jint jMask = (jint)eventMask;

	// event mask setting
	pleClient->event_mask = eventMask;
    
	// setEventHandler
	//pthread_mutex_lock(&creationLock);
	if (invokeMethod(env, NULL, INSTANCE, jClient, PLEIADES_CLIENT, 
				"setEventHandler", 
				JMETHOD2(JPARAM(PLEIADES_EVENT_HANDLER), "I", "V"), 
				jHandler, jMask) != 0) { 
        fprintf(stderr, "Call to com.nhncorp.pleiades.client.LockService::setEventHandler failed!\n");
        errno = EINTERNAL;
		//pthread_mutex_unlock(&creationLock);
        return -1;
    }
	//pthread_mutex_unlock(&creationLock);

	return 0;
}

static int getLockMetaData(JNIEnv *env, jobject jClient, jobject jMetaData, 
							pleiades_lock_meta_data_t *metaData)
{
    jvalue  jVal;
    jstring     jCreator;
    const char *cCreator;
    jstring     jLockId;
    const char *cLockId;
    jstring     jContentKey;
    const char *cContentKey;

    if (invokeMethod(env, &jVal, INSTANCE, jMetaData, PLEIADES_METADATA, 
				"getCreator", "()Ljava/lang/String;") != 0) {
		fprintf(stderr, "Call to com.nhncorp.pleiades.protocol.pleiades_lock_meta_data_t::getCreator failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    jCreator = jVal.l;
    cCreator = (const char*)((*env)->GetStringUTFChars(env, jCreator, NULL));
    metaData->creator = strdup(cCreator);
    (*env)->ReleaseStringUTFChars(env, jCreator, cCreator);
    destroyLocalReference(env, jCreator);
    
    if (invokeMethod(env, &jVal, INSTANCE, jMetaData, PLEIADES_METADATA, 
				"getLockId", "()Ljava/lang/String;") != 0) {
		fprintf(stderr, "Call to com.nhncorp.pleiades.protocol.pleiades_lock_meta_data_t::getLockId failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    jLockId = jVal.l;
    cLockId = (const char*)((*env)->GetStringUTFChars(env, jLockId, NULL));
    metaData->lock_id = strdup(cLockId);
    (*env)->ReleaseStringUTFChars(env, jLockId, cLockId);
    destroyLocalReference(env, jLockId);
    
    if (invokeMethod(env, &jVal, INSTANCE, jMetaData, PLEIADES_METADATA, 
				"getLockMode", "()I") != 0) {
		fprintf(stderr, "Call to com.nhncorp.pleiades.protocol.pleiades_lock_meta_data_t::getLockMode failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    metaData->lock_mode = jVal.i;
    
    if (invokeMethod(env, &jVal, INSTANCE, jMetaData, PLEIADES_METADATA, 
				"getGenerationNumber", "()J") != 0) {
		fprintf(stderr, "Call to com.nhncorp.pleiades.protocol.pleiades_lock_meta_data_t::getGenerationNumber failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    metaData->generation_number = jVal.j;

    if (invokeMethod(env, &jVal, INSTANCE, jMetaData, PLEIADES_METADATA, 
				"getContentKey", "()Ljava/lang/String;") != 0) {
		fprintf(stderr, "Call to com.nhncorp.pleiades.protocol.pleiades_lock_meta_data_t::getContentKey failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    jContentKey = jVal.l;
    cContentKey = 
		(const char*)((*env)->GetStringUTFChars(env, jContentKey, NULL));
    metaData->content_key = strdup(cContentKey);
    (*env)->ReleaseStringUTFChars(env, jContentKey, cContentKey);
    destroyLocalReference(env, jContentKey);

    return 0;
}

static int setLockMetaData(JNIEnv *env, jobject jClient, jobject jMeta, 
		const char *path, const char *key)
{
    jvalue  jVal;
    jobject jMetaData = (jobject)jMeta;
	jstring jContentKey = (*env)->NewStringUTF(env, key);

    if (invokeMethod(env, &jVal, INSTANCE, jMetaData, PLEIADES_METADATA, 
				"setContentKey", "(Ljava/lang/String;)V", 
				jContentKey) != 0) {
		fprintf(stderr, "Call to com.nhncorp.pleiades.protocol.pleiades_lock_meta_data_t::setContentKey failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    destroyLocalReference(env, jContentKey);

    return 0;
}

int getLockEventData(JNIEnv *env, jobject jHandler, 
						jobject jEventData, pleiades_lock_event_data_t *eventData)
{    
    jstring     jHostname;
    const char *cHostname;
    jvalue  jVal;
    jstring     jLockId;
    const char *cLockId;

    if (invokeMethod(env, &jVal, INSTANCE, jEventData, PLEIADES_EVENTDATA, 
				"getHostname", "()Ljava/lang/String;") != 0) {
		fprintf(stderr, "Call to com.nhncorp.pleiades.client.pleiades_lock_event_data_t::getHostname failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    jHostname = jVal.l;
    cHostname = (const char*)((*env)->GetStringUTFChars(env,jHostname,NULL));
    eventData->creator = strdup(cHostname);
    (*env)->ReleaseStringUTFChars(env, jHostname, cHostname);
    destroyLocalReference(env, jHostname);
    
    if (invokeMethod(env, &jVal, INSTANCE, jEventData, PLEIADES_EVENTDATA, 
				"getLockId", "()Ljava/lang/String;") != 0) {
		fprintf(stderr, "Call to com.nhncorp.pleiades.client.pleiades_lock_event_data_t::getLockId failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    jLockId = jVal.l;
    cLockId = (const char*)((*env)->GetStringUTFChars(env, jLockId, NULL));
    eventData->lock_id = strdup(cLockId);
    (*env)->ReleaseStringUTFChars(env, jLockId, cLockId);
    destroyLocalReference(env, jLockId);
    
    if (invokeMethod(env, &jVal, INSTANCE, jEventData,
                     PLEIADES_EVENTDATA, "isDirectory", "()Z") != 0) {
		fprintf(stderr, "Call to com.nhncorp.pleiades.client.pleiades_lock_event_data_t::isDirectory failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    eventData->is_dir = (jVal.z) ? true : false;
    if (invokeMethod(env, &jVal, INSTANCE, jEventData, PLEIADES_EVENTDATA, 
				"getGenerationNumber", "()J") != 0) {
		fprintf(stderr, "Call to com.nhncorp.pleiades.client.pleiades_lock_event_data_t::getGenerationNumber failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    eventData->generation_number = jVal.j;
    
    if (invokeMethod(env, &jVal, INSTANCE, jEventData, PLEIADES_EVENTDATA, 
				"getEventType", "()I") != 0) {
		fprintf(stderr, "Call to com.nhncorp.pleiades.client.pleiades_lock_event_data_t::getEventType failed!\n");
        errno = EINTERNAL;
        return -1;
    }

    eventData->type = jVal.i;
  
	return 0;
}

void pleiades_free_lock_status(pleiades_lock_status_t *lockStatus, 
								int numEntries)
{
    int i;

    // Free the creator, lockId
    for (i = 0; i < numEntries; i++) {
        if (lockStatus[i].creator) 
            free(lockStatus[i].creator);

        if (lockStatus[i].lock_id) 
            free(lockStatus[i].lock_id);
    }

    // Free entire block
    free(lockStatus);
}

void pleiades_free_lock_meta_data(pleiades_lock_meta_data_t *metaData, 
								int numEntries)
{
    int i;

    // Free the creator, lockId, contentKey
    for (i = 0; i < numEntries; i++) {
        if (metaData[i].creator) 
            free(metaData[i].creator);

        if (metaData[i].lock_id) 
            free(metaData[i].lock_id);

        if (metaData[i].content_key) 
            free(metaData[i].content_key);
    }

    // Free entire block
    free(metaData);
}

void pleiades_free_lock_event_data(pleiades_lock_event_data_t *eventData, 
								int numEntries)
{
    int i;
	
    // Free the creator, lockId
    for (i = 0; i < numEntries; i++) {
        if (eventData[i].creator)
            free(eventData[i].creator);

        if (eventData[i].lock_id)
            free(eventData[i].lock_id);
    }

    // Free entire block
    free(eventData);
}

void pleiades_set_contents_modified(pleiades_event_handler_t *handler, 
						void (*func)(pleiades_lock_event_data_t *))
{
	handler->contents_modified = func;
}

void pleiades_set_node_added(pleiades_event_handler_t *handler, 
						void (*func)(pleiades_lock_event_data_t *))
{
	handler->node_added = func;
}

void pleiades_set_node_removed(pleiades_event_handler_t *handler, 
						void (*func)(pleiades_lock_event_data_t *))
{
	handler->node_removed = func;
}

void pleiades_set_node_modified(pleiades_event_handler_t *handler, 
						void (*func)(pleiades_lock_event_data_t *))
{
	handler->node_modified = func;
}

void pleiades_set_master_failover(pleiades_event_handler_t *handler, 
						void (*func)())
{
	handler->master_failover = func;
}

void pleiades_set_session_warning(pleiades_event_handler_t *handler, 
						void (*func)())
{
	handler->session_warning = func;
}

void pleiades_set_session_safe(pleiades_event_handler_t *handler, 
						void (*func)())
{
	handler->session_safe = func;
}

void pleiades_set_session_expired(pleiades_event_handler_t *handler, 
						void (*func)())
{
	handler->session_expired = func;
}

void pleiades_set_client_expired(pleiades_event_handler_t *handler, 
						void (*func)(pleiades_lock_event_data_t *))
{
	handler->client_expired = func;
}

void pleiades_set_lock_expired(pleiades_event_handler_t *handler, 
						void (*func)(pleiades_lock_event_data_t *))
{
	handler->lock_expired = func;
}

void pleiades_unset_contents_modified(pleiades_event_handler_t *handler) 
{
	handler->contents_modified = NULL;
}

void pleiades_unset_node_added(pleiades_event_handler_t *handler) 
{
	handler->node_added = NULL;
}

void pleiades_unset_node_removed(pleiades_event_handler_t *handler) 
{
	handler->node_removed = NULL;
}

void pleiades_unset_node_modified(pleiades_event_handler_t *handler) 
{
	handler->node_modified = NULL;
}

void pleiades_unset_master_failover(pleiades_event_handler_t *handler) 
{
	handler->master_failover = NULL;
}

void pleiades_unset_session_warning(pleiades_event_handler_t *handler) 
{
	handler->session_warning = NULL;
}

void pleiades_unset_session_safe(pleiades_event_handler_t *handler) 
{
	handler->session_safe = NULL;
}

void pleiades_unset_session_expired(pleiades_event_handler_t *handler) 
{
	handler->session_expired = NULL;
}

void pleiades_unset_client_expired(pleiades_event_handler_t *handler) 
{
	handler->client_expired = NULL;
}

void pleiades_unset_lock_expired(pleiades_event_handler_t *handler) 
{
	handler->lock_expired = NULL;
}

void pleiades_unset_all_events(pleiades_event_handler_t *handler)
{
	handler->contents_modified = NULL;
	handler->node_added = NULL;
	handler->node_removed = NULL;
	handler->node_modified = NULL;
	handler->master_failover = NULL;
	handler->session_warning = NULL;
	handler->session_safe = NULL;
	handler->session_expired = NULL;
	handler->client_expired = NULL;
	handler->lock_expired = NULL;
}

pleiades_constraint_t *pleiades_create_constraint()
{
	pleiades_constraint_t *result = calloc(1, sizeof(pleiades_constraint_t));
	INIT_LIST_HEAD(&result->cond_list);
	return result;
}

void pleiades_destroy_constraint(pleiades_constraint_t *constr)
{
	pleiades_condition_t *pos, *n;

	list_for_each_entry_safe(pos, n, &constr->cond_list, 
						pleiades_condition_t, link) {
		list_del(&pos->link);
		free(pos->key);
		free(pos);
	}

	free(constr);
}

void pleiades_add_condition(pleiades_constraint_t *constr, 
							pleiades_operator_t op, const char *key) 
{
	pleiades_condition_t *cond = calloc(1, sizeof(pleiades_condition_t));
	cond->op = op;
	cond->key = strdup(key);
	list_add_tail(&cond->link, &constr->cond_list);
}

pleiades_lock_event_filter_t *pleiades_create_lock_event_filter()
{
	pleiades_lock_event_filter_t *result = 
						calloc(1, sizeof(pleiades_lock_event_filter_t));
	INIT_LIST_HEAD(&result->constr_list);
	return result;
}

void pleiades_destroy_lock_event_filter(pleiades_lock_event_filter_t *filter) 
{
	pleiades_constraint_t *pos, *n;
	list_for_each_entry_safe(pos, n, &filter->constr_list,pleiades_constraint_t,link) {
		list_del(&pos->link);
		pleiades_destroy_constraint(pos);
	}

	free(filter);
}

void pleiades_add_constraint(pleiades_lock_event_filter_t *filter, 
				pleiades_constraint_t *constr) 
{
	list_add_tail(&constr->link, &filter->constr_list);
}

void pleiades_remove_constraint(pleiades_lock_event_filter_t *filter, 
		pleiades_operator_t op)
{
	pleiades_constraint_t *pos, *n;
	pleiades_condition_t *ppos, *nn;

	list_for_each_entry_safe(pos, n, &filter->constr_list,
							pleiades_constraint_t,link) {
		list_for_each_entry_safe(ppos, nn, &pos->cond_list,
							pleiades_condition_t,link) {
			if (ppos->op == op) {
				list_del(&ppos->link);
				free(ppos);
			}
		}

		if (list_empty(&pos->cond_list)) {
			list_del(&pos->link);
			free(pos);
		}
	}
}

void pleiades_add_lock_event_monitor(pleiades_event_handler_t *handler, 
		 void (*eventMonitor)(pleiades_event_t, pleiades_lock_event_data_t *),
		 pleiades_lock_event_filter_t *filter)
{
	pleiades_lock_event_monitor_t *lem = 
					calloc(1, sizeof(pleiades_lock_event_monitor_t)); 
	lem->notify_event = eventMonitor;
	lem->filter = filter;
	list_add_tail(&lem->link, &handler->event_monitors);
}

void pleiades_remove_event_monitor(pleiades_event_handler_t *handler, 
		void (*eventMonitor)(pleiades_event_t, pleiades_lock_event_data_t *))
{
	pleiades_lock_event_monitor_t *pos, *n;

	list_for_each_entry_safe(pos, n, &handler->event_monitors, 
							pleiades_lock_event_monitor_t, link) {
		if (pos->notify_event == eventMonitor) {
			list_del(&pos->link);
			pleiades_destroy_lock_event_filter(pos->filter);
			free(pos);
			break;
		}
	}
}
